home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
JFC.bin
/
DefaultTreeSelectionModel.java
< prev
next >
Wrap
Text File
|
1998-06-30
|
31KB
|
1,051 lines
/*
* @(#)DefaultTreeSelectionModel.java 1.18 98/02/02
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.tree;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.BitSet;
import java.util.EventListener;
import java.util.Vector;
import com.sun.java.swing.event.*;
import com.sun.java.swing.DefaultListSelectionModel;
/**
* Implementation of TreeSelectionModel. Listeners are notified whenever
* the paths in the selection change, not the rows. In order
* to be able to track row changes you may wish to become a listener
* for expansion events on the tree and test for changes from there.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @version 1.18 02/02/98
* @author Scott Violet
*/
public class DefaultTreeSelectionModel extends Object implements Cloneable, Serializable, TreeSelectionModel
{
/** Property name for selectionMode. */
public static final String SELECTION_MODE_PROPERTY = "selectionMode";
/** Used to messaged registered listeners. */
protected PropertyChangeSupport changeSupport;
/** Paths that are currently selected. Will be null if nothing is
* currently selected. */
protected TreePath[] selection;
/** Event listener list. */
protected EventListenerList listenerList = new EventListenerList();
/** Provides a row for a given path. */
transient protected RowMapper rowMapper;
/** Handles maintaining the list selection model. */
protected DefaultListSelectionModel listSelectionModel;
/** Mode for the selection, will be either SINGLE_TREE_SELECTION,
* CONTIGUOUS_TREE_SELECTION or DISCONTIGUOUS_TREE_SELECTION.
*/
protected int selectionMode;
/** Last path that was added. */
protected TreePath leadPath;
/** Index of the lead path in selection. */
protected int leadIndex;
/** Lead row. */
protected int leadRow;
public DefaultTreeSelectionModel() {
listSelectionModel = new DefaultListSelectionModel();
selectionMode = DISCONTIGUOUS_TREE_SELECTION;
leadIndex = leadRow = -1;
}
/**
* Sets the RowMapper instance. This instance is used to determine
* what row corresponds to what path.
*/
public void setRowMapper(RowMapper newMapper) {
rowMapper = newMapper;
resetRowSelection();
}
/**
* Returns the RowMapper instance that is able to map a path to a
* row.
*/
public RowMapper getRowMapper() {
return rowMapper;
}
/**
* Sets the selection model, which must be one of SINGLE_TREE_SELECTION,
* CONTIGUOUS_TREE_SELECTION or DISCONTIGUOUS_TREE_SELECTION.
*/
public void setSelectionMode(int mode) {
int oldMode = selectionMode;
selectionMode = mode;
if(selectionMode != TreeSelectionModel.SINGLE_TREE_SELECTION &&
selectionMode != TreeSelectionModel.CONTIGUOUS_TREE_SELECTION &&
selectionMode != TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
selectionMode = TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION;
if(oldMode != selectionMode && changeSupport != null)
changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY,
new Integer(oldMode),
new Integer(selectionMode));
}
/**
* Returns the selection mode.
*/
public int getSelectionMode() {
return selectionMode;
}
/**
* Sets the selection to path. If this represents a change, then
* the TreeSelectionListeners are notified.
*
* @param path new path to select
*/
public void setSelectionPath(TreePath path) {
if(path == null)
setSelectionPaths(null);
else {
TreePath[] newPaths = new TreePath[1];
newPaths[0] = path;
setSelectionPaths(newPaths);
}
}
/**
* Sets the selection to the paths in paths. If this represents a
* change the TreeSelectionListeners are notified. Potentially
* paths will be held by the reciever, in other words don't change
* any of the objects in the array once passed in.
*
* @param paths new selection.
*/
public void setSelectionPaths(TreePath[] pPaths) {
boolean differs = false;
int newCount, newCounter, oldCount, oldCounter;
TreePath[] paths = pPaths;
if(paths == null)
newCount = 0;
else
newCount = paths.length;
if(selection == null)
oldCount = 0;
else
oldCount = selection.length;
if((newCount + oldCount) != 0) {
if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION) {
/* If single selection and more than one path, only allow
first. */
if(newCount > 1) {
paths = new TreePath[1];
paths[0] = pPaths[0];
newCount = 1;
}
}
else if(selectionMode ==
TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) {
/* If contiguous selection and paths aren't contiguous,
only select the first path item. */
if(newCount > 0 && !arePathsContiguous(paths)) {
paths = new TreePath[1];
paths[0] = pPaths[0];
newCount = 1;
}
}
boolean found;
int validCount = 0;
TreePath beginLeadPath = leadPath;
Vector cPaths = new Vector(newCount + oldCount);
leadPath = null;
/* Find the paths that are new. */
for(newCounter = 0; newCounter < newCount; newCounter++) {
found = false;
if(paths[newCounter] != null) {
validCount++;
for(oldCounter = 0; oldCounter < oldCount; oldCounter++) {
if(selection[oldCounter] != null &&
selection[oldCounter].equals(paths[newCounter])) {
selection[oldCounter] = null;
oldCounter = oldCount;
found = true;
}
}
if(!found)
cPaths.addElement(new PathPlaceHolder
(paths[newCounter], true));
if(leadPath == null)
leadPath = paths[newCounter];
}
}
/* Get the paths that were selected but no longer selected. */
for(oldCounter = 0; oldCounter < oldCount; oldCounter++)
if(selection[oldCounter] != null)
cPaths.addElement(new PathPlaceHolder
(selection[oldCounter], false));
/* If the validCount isn't equal to newCount it means there
are some null in paths, remove them and set selection to
the new path. */
if(validCount == 0)
selection = null;
else if (validCount != newCount) {
selection = new TreePath[validCount];
for(newCounter = 0, validCount = 0; newCounter < newCount;
newCounter++)
if(paths[newCounter] != null)
selection[validCount++] = paths[newCounter];
}
else {
selection = new TreePath[paths.length];
System.arraycopy(paths, 0, selection, 0, paths.length);
}
if(selection != null)
insureUniqueness();
updateLeadIndex();
resetRowSelection();
/* Notify of the change. */
if(cPaths.size() > 0)
notifyPathChange(cPaths, beginLeadPath);
}
}
/**
* Adds path to the current selection. If path is not currently
* in the selection the TreeSelectionListeners are notified.
*
* @param path the new path to add to the current selection.
*/
public void addSelectionPath(TreePath path) {
if(path != null) {
TreePath[] toAdd = new TreePath[1];
toAdd[0] = path;
addSelectionPaths(toAdd);
}
}
/**
* Adds paths to the current selection. If any of the paths in
* paths are not currently in the selection the TreeSelectionListeners
* are notified.
*
* @param path the new path to add to the current selection.
*/
public void addSelectionPaths(TreePath[] paths) {
int newPathLength = ((paths == null) ? 0 : paths.length);
if(newPathLength > 0) {
if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION &&
selection != null && selection.length > 0)
setSelectionPaths(paths);
else if(selectionMode == TreeSelectionModel.
CONTIGUOUS_TREE_SELECTION && !canPathsBeAdded(paths)) {
if(arePathsContiguous(paths))
setSelectionPaths(paths);
else {
TreePath[] newPaths = new TreePath[1];
newPaths[0] = paths[0];
setSelectionPaths(newPaths);
}
}
else {
boolean didCopyPaths, inSelection;
int counter, validCount;
int oldCount, oldCounter;
TreePath beginLeadPath = leadPath;
Vector cPaths = null;
if(selection == null)
oldCount = 0;
else
oldCount = selection.length;
didCopyPaths = false;
leadPath = null;
/* Determine the paths that aren't currently in the
selection. */
for(counter = 0, validCount = 0; counter < paths.length;
counter++) {
if(paths[counter] != null) {
inSelection = false;
for (oldCounter = 0; oldCounter < oldCount;
oldCounter++) {
if(paths[counter].equals(selection[oldCounter])) {
oldCounter = oldCount;
if(!didCopyPaths) {
/* Copy the paths so that we can mess with
the array. */
TreePath[] copiedPaths;
copiedPaths = new TreePath[paths.length];
System.arraycopy(paths, 0, copiedPaths,
0, paths.length);
paths = copiedPaths;
didCopyPaths = true;
}
paths[counter] = null;
inSelection = true;
}
}
if(!inSelection) {
validCount++;
if(cPaths == null)
cPaths = new Vector();
cPaths.addElement(new PathPlaceHolder
(paths[counter], true));
}
if(leadPath == null)
leadPath = paths[counter];
}
}
if(leadPath == null)
leadPath = beginLeadPath;
if(validCount > 0) {
TreePath newSelection[] = new TreePath[oldCount +
validCount];
/* And build the new selection. */
if(oldCount > 0)
System.arraycopy(selection, 0, newSelection, 0,
oldCount);
if(validCount != paths.length) {
/* Some of the paths in paths are already in
the selection. */
int validCounter;
for(counter = validCounter = 0; counter < paths.length;
counter++) {
if (paths[counter] != null)
newSelection[oldCount + validCounter++] =
paths[counter];
}
}
else
System.arraycopy(paths, 0, newSelection, oldCount,
validCount);
selection = newSelection;
insureUniqueness();
updateLeadIndex();
resetRowSelection();
notifyPathChange(cPaths, beginLeadPath);
}
else
leadPath = beginLeadPath;
}
}
}
/**
* Removes path from the selection. If path is in the selection
* The TreeSelectionListeners are notified.
*
* @param path the path to remove from the selection.
*/
public void removeSelectionPath(TreePath path) {
if(path != null) {
TreePath[] rPath = new TreePath[1];
rPath[0] = path;
removeSelectionPaths(rPath);
}
}
/**
* Removes paths from the selection. If any of the paths in paths
* are in the selection the TreeSelectionListeners are notified.
*
* @param path the path to remove from the selection.
*/
public void removeSelectionPaths(TreePath[] paths) {
if (paths != null && selection != null && paths.length > 0) {
if(!canPathsBeRemoved(paths)) {
/* Could probably do something more interesting here! */
clearSelection();
}
else {
int oldCount, oldCounter;
int removeCount, removeCounter;
int toRemoveCount = 0;
TreePath beginLeadPath = leadPath;
Vector pathsToRemove = null;
oldCount = selection.length;
/* Find the paths that can be removed. */
for (removeCounter = 0; removeCounter < paths.length;
removeCounter++) {
if(paths[removeCounter] != null) {
if(leadPath != null &&
leadPath.equals(paths[removeCounter]))
leadPath = null;
for(oldCounter = 0; oldCounter < oldCount;
oldCounter++) {
if(paths[removeCounter].equals
(selection[oldCounter])){
selection[oldCounter] = null;
oldCounter = oldCount;
if(pathsToRemove == null)
pathsToRemove = new Vector(paths.length);
if(!pathsToRemove.contains
(paths[removeCounter]))
pathsToRemove.addElement
(new PathPlaceHolder
(paths[removeCounter], false));
}
}
}
}
if(pathsToRemove != null) {
removeCount = pathsToRemove.size();
if(removeCount == selection.length)
selection = null;
else {
int validCount = 0;
TreePath[] newSelection;
newSelection = new TreePath[selection.length -
removeCount];
for(oldCounter = 0; oldCounter < oldCount;
oldCounter++)
if(selection[oldCounter] != null)
newSelection[validCount++] =
selection[oldCounter];
selection = newSelection;
}
if(leadPath == null && selection != null)
leadPath = selection[0];
updateLeadIndex();
resetRowSelection();
notifyPathChange(pathsToRemove, beginLeadPath);
}
}
}
}
/**
* Returns the first path in the selection.
*/
public TreePath getSelectionPath() {
if(selection != null)
return selection[0];
return null;
}
/**
* Returns the paths in the selection.
*/
public TreePath[] getSelectionPaths() {
if(selection != null) {
int pathSize = selection.length;
TreePath[] result = new TreePath[pathSize];
System.arraycopy(selection, 0, result, 0, pathSize);
return result;
}
return null;
}
/**
* Returns the number of paths that are selected.
*/
public int getSelectionCount() {
return (selection == null) ? 0 : selection.length;
}
/**
* Returns true if the path, path, is in the current selection.
*/
public boolean isPathSelected(TreePath path) {
if (selection != null) {
for (int counter = 0; counter < selection.length; counter++)
if(selection[counter].equals(path))
return true;
}
return false;
}
/**
* Returns true if the selection is currently empty.
*/
public boolean isSelectionEmpty() {
return (selection == null);
}
/**
* Empties the current selection. If this represents a change in the
* current selection, the selection listeners are notified.
*/
public void clearSelection() {
if(selection != null) {
int selSize = selection.length;
boolean[] newness = new boolean[selSize];
for(int counter = 0; counter < selSize; counter++)
newness[counter] = false;
TreeSelectionEvent event = new TreeSelectionEvent
(this, selection, newness, leadPath, null);
leadPath = null;
leadIndex = leadRow = -1;
selection = null;
resetRowSelection();
fireValueChanged(event);
}
}
/**
* Adds x to the list of listeners that are notified each time the
* selection changes.
*
* @param x the new listener to be added.
*/
public void addTreeSelectionListener(TreeSelectionListener x) {
listenerList.add(TreeSelectionListener.class, x);
}
/**
* Removes x from the list of listeners that are notified each time
* the selection changes.
*
* @param x the listener to remove.
*/
public void removeTreeSelectionListener(TreeSelectionListener x) {
listenerList.remove(TreeSelectionListener.class, x);
}
/*
* Notify all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
* @see EventListenerList
*/
protected void fireValueChanged(TreeSelectionEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// TreeSelectionEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==TreeSelectionListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((TreeSelectionListener)listeners[i+1]).valueChanged(e);
}
}
}
/**
* Returns all of the currently selected rows.
*/
public int[] getSelectionRows() {
// This is currently rather expensive. Needs
// to be better support from ListSelectionModel to speed this up.
if(rowMapper != null && selection != null) {
return rowMapper.getRowsForPaths(selection);
}
return null;
}
/**
* Gets the first selected row.
*/
public int getMinSelectionRow() {
return listSelectionModel.getMinSelectionIndex();
}
/**
* Gets the last selected row.
*/
public int getMaxSelectionRow() {
return listSelectionModel.getMaxSelectionIndex();
}
/**
* Returns true if the row identitifed by row is selected.
*/
public boolean isRowSelected(int row) {
return listSelectionModel.isSelectedIndex(row);
}
/**
* Recalculates what rows are selected by asking the RowMapper for the
* row for each path.
*/
public void resetRowSelection() {
listSelectionModel.clearSelection();
if(selection != null && rowMapper != null) {
int aRow;
int validCount = 0;
int[] rows = rowMapper.getRowsForPaths(selection);
for(int counter = 0, maxCounter = selection.length;
counter < maxCounter; counter++) {
aRow = rows[counter];
if(aRow != -1)
listSelectionModel.addSelectionInterval(aRow, aRow);
}
if(leadIndex != -1)
leadRow = rows[leadIndex];
insureRowContinuity();
}
else
leadRow = -1;
}
/**
* Returns the lead selection index. That is the last index that was
* added.
*/
public int getLeadSelectionRow() {
return leadRow;
}
/**
* Returns the last path that was added.
*/
public TreePath getLeadSelectionPath() {
return leadPath;
}
/**
* Add a PropertyChangeListener to the listener list.
* The listener is registered for all properties.
* <p>
* A PropertyChangeEvent will get fired in response to an
* explicit setFont, setBackground, or SetForeground on the
* current component. Note that if the current component is
* inheriting its foreground, background, or font from its
* container, then no event will be fired in response to a
* change in the inherited property.
*
* @param listener The PropertyChangeListener to be added
*/
public synchronized void addPropertyChangeListener(
PropertyChangeListener listener) {
if (changeSupport == null) {
changeSupport = new java.beans.PropertyChangeSupport(this);
}
changeSupport.addPropertyChangeListener(listener);
}
/**
* Remove a PropertyChangeListener from the listener list.
* This removes a PropertyChangeListener that was registered
* for all properties.
*
* @param listener The PropertyChangeListener to be removed
*/
public synchronized void removePropertyChangeListener(
PropertyChangeListener listener) {
if (changeSupport == null) {
return;
}
changeSupport.removePropertyChangeListener(listener);
}
/**
* Useful for CONTIGUOUS_TREE_SELECTION. If the rows that are selected
* are not contiguous then the selection is reset to be contiguous.
* Or if the selection mode is single selection and more than one
* this is selected the selection is reset.
*/
protected void insureRowContinuity() {
if(selectionMode == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION &&
selection != null && rowMapper != null) {
DefaultListSelectionModel lModel = listSelectionModel;
int min = lModel.getMinSelectionIndex();
if(min != -1) {
for(int counter = min,
maxCounter = lModel.getMaxSelectionIndex();
counter <= maxCounter; counter++) {
if(!lModel.isSelectedIndex(counter)) {
if(counter == min) {
clearSelection();
}
else {
TreePath[] newSel = new TreePath[counter - min];
System.arraycopy(selection, 0, newSel,
0, counter - min);
setSelectionPaths(newSel);
break;
}
}
}
}
}
else if(selectionMode == TreeSelectionModel.SINGLE_TREE_SELECTION &&
selection != null && selection.length > 1) {
setSelectionPath(selection[0]);
}
}
/**
* Returns true if the paths are contiguous.
*/
protected boolean arePathsContiguous(TreePath[] paths) {
if(rowMapper == null || paths.length < 2)
return true;
else {
BitSet bitSet = new BitSet(32);
int anIndex, counter, min;
int pathCount = paths.length;
int validCount = 0;
TreePath[] tempPath = new TreePath[1];
tempPath[0] = paths[0];
min = rowMapper.getRowsForPaths(tempPath)[0];
for(counter = 0; counter < pathCount; counter++) {
if(paths[counter] != null) {
tempPath[0] = paths[counter];
anIndex = rowMapper.getRowsForPaths(tempPath)[0];
if(anIndex == -1 || anIndex < (min - pathCount) ||
anIndex > (min + pathCount))
return false;
if(anIndex < min)
min = anIndex;
if(!bitSet.get(anIndex)) {
bitSet.set(anIndex);
validCount++;
}
}
}
int maxCounter = validCount + min;
for(counter = min; counter < maxCounter; counter++)
if(!bitSet.get(counter))
return false;
}
return true;
}
/**
* Returns true if the paths can be added without breaking the
* continuity of the model.
*/
protected boolean canPathsBeAdded(TreePath[] paths) {
if(paths == null || paths.length == 0 || rowMapper == null ||
selection == null || selectionMode ==
TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
return true;
else {
BitSet bitSet = new BitSet();
DefaultListSelectionModel lModel = listSelectionModel;
int anIndex;
int counter;
int min = lModel.getMinSelectionIndex();
int max = lModel.getMaxSelectionIndex();
TreePath[] tempPath = new TreePath[1];
if(min != -1) {
for(counter = min; counter <= max; counter++) {
if(lModel.isSelectedIndex(min))
bitSet.set(counter);
}
}
else {
tempPath[0] = paths[0];
min = max = rowMapper.getRowsForPaths(tempPath)[0];
}
for(counter = paths.length - 1; counter >= 0; counter--) {
if(paths[counter] != null) {
tempPath[0] = paths[counter];
anIndex = rowMapper.getRowsForPaths(tempPath)[0];
min = Math.min(anIndex, min);
max = Math.max(anIndex, max);
if(anIndex == -1)
return false;
bitSet.set(anIndex);
}
}
for(counter = min; counter <= max; counter++)
if(!bitSet.get(counter))
return false;
}
return true;
}
/**
* Returns true if the paths can be removed without breaking the
* continuity of the model.
* This is rather expensive.
*/
protected boolean canPathsBeRemoved(TreePath[] paths) {
if(rowMapper == null || selection == null ||
selectionMode == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
return true;
else {
boolean found;
BitSet bitSet = new BitSet();
int counter, rCounter;
int pathCount = paths.length;
int anIndex;
int min = -1;
int validCount = 0;
TreePath[] tPaths = new TreePath[pathCount];
TreePath[] tempPath = new TreePath[1];
/* Determine the rows for the removed entries. */
System.arraycopy(paths, 0, tPaths, 0, pathCount);
for(counter = selection.length - 1; counter >= 0; counter--) {
found = false;
for(rCounter = 0; rCounter < pathCount; rCounter++) {
if(tPaths[rCounter] != null && selection[counter].
equals(tPaths[rCounter])) {
tPaths[rCounter] = null;
found = true;
break;
}
}
if(!found) {
tempPath[0] = selection[counter];
anIndex = rowMapper.getRowsForPaths(tempPath)[0];
if(anIndex != -1 && !bitSet.get(anIndex)) {
validCount++;
if(min == -1)
min = anIndex;
else
min = Math.min(min, anIndex);
bitSet.set(anIndex);
}
}
}
/* Make sure they are contiguous. */
if(validCount > 1) {
for(counter = min + validCount - 1; counter >= min;
counter--)
if(!bitSet.get(counter))
return false;
}
}
return true;
}
/**
* Notifies listeners of a change in path. changePaths should contain
* instances of PathPlaceHolder.
*
*/
protected void notifyPathChange(Vector changedPaths,
TreePath oldLeadSelection) {
int cPathCount = changedPaths.size();
boolean[] newness = new boolean[cPathCount];
TreePath[] paths = new TreePath[cPathCount];
PathPlaceHolder placeholder;
for(int counter = 0; counter < cPathCount; counter++) {
placeholder = (PathPlaceHolder)changedPaths.elementAt(counter);
newness[counter] = placeholder.isNew;
paths[counter] = placeholder.path;
}
TreeSelectionEvent event = new TreeSelectionEvent
(this, paths, newness, oldLeadSelection, leadPath);
fireValueChanged(event);
}
/**
* Updates the leadIndex instance variable.
*/
protected void updateLeadIndex() {
if(leadPath != null) {
if(selection == null) {
leadPath = null;
leadIndex = leadRow = -1;
}
else {
leadRow = leadIndex = -1;
for(int counter = selection.length - 1; counter >= 0;
counter--) {
if(selection[counter].equals(leadPath)) {
leadIndex = counter;
break;
}
}
}
}
}
/**
* Insures that all the elements in path are unique. This does not
* check for a null selection!
*/
protected void insureUniqueness() {
int compareCounter;
int dupCount = 0;
int indexCounter;
for(compareCounter = 0; compareCounter < selection.length;
compareCounter++) {
if(selection[compareCounter] != null) {
for(indexCounter = compareCounter + 1; indexCounter <
selection.length; indexCounter++) {
if (selection[indexCounter] != null &&
selection[compareCounter].equals(selection
[indexCounter])){
dupCount++;
selection[indexCounter] = null;
}
}
}
}
if(dupCount > 0) {
/* Squash the duplicates. */
TreePath[] newSelection = new TreePath[selection.length-
dupCount];
for (int counter = 0, validCounter = 0; counter < selection.length;
counter++)
if(selection[counter] != null)
newSelection[validCounter++] = selection[counter];
selection = newSelection;
}
}
public String toString() {
int selCount = getSelectionCount();
StringBuffer retBuffer = new StringBuffer();
int[] rows;
if(rowMapper != null)
rows = rowMapper.getRowsForPaths(selection);
else
rows = null;
retBuffer.append(getClass().getName() + " " + hashCode() + " [ ");
for(int counter = 0; counter < selCount; counter++) {
if(rows != null)
retBuffer.append(selection[counter].toString() + "@" +
Integer.toString(rows[counter])+ " ");
else
retBuffer.append(selection[counter].toString() + " ");
}
retBuffer.append("]");
return retBuffer.toString();
}
/**
* Returns a clone of the reciever with the same selection.
* selectionListeners, and PropertyListeners are not duplicated.
*
* @exception CloneNotSupportedException if the receiver does not
* both (a) implement the Cloneable interface and (b) define a
* <code>clone</code> method.
*/
public Object clone() throws CloneNotSupportedException {
DefaultTreeSelectionModel clone = (DefaultTreeSelectionModel)
super.clone();
clone.changeSupport = null;
if(selection != null) {
int selLength = selection.length;
clone.selection = new TreePath[selLength];
System.arraycopy(selection, 0, clone.selection, 0, selLength);
}
clone.listenerList = new EventListenerList();
clone.listSelectionModel = (DefaultListSelectionModel)
listSelectionModel.clone();
return clone;
}
// Serialization support.
private void writeObject(ObjectOutputStream s) throws IOException {
Object[] tValues;
s.defaultWriteObject();
// Save the rowMapper, if it implements Serializable
if(rowMapper != null && rowMapper instanceof Serializable) {
tValues = new Object[2];
tValues[0] = "rowMapper";
tValues[1] = rowMapper;
}
else
tValues = new Object[0];
s.writeObject(tValues);
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
Object[] tValues;
s.defaultReadObject();
tValues = (Object[])s.readObject();
if(tValues.length > 0 && tValues[0].equals("rowMapper"))
rowMapper = (RowMapper)tValues[1];
}
}
/**
* Holds a path and whether or not it is new.
*/
class PathPlaceHolder {
protected boolean isNew;
protected TreePath path;
PathPlaceHolder(TreePath path, boolean isNew) {
this.path = path;
this.isNew = isNew;
}
}